-module(ta_log).
-behaviour(gen_event).

-export([start_link/0, stop/0]).
-export([debug/2, info/2, warn/2, error/2]).

-export([init/1, handle_event/2, terminate/2, code_change/3]).
-export([handle_info/2, handle_call/2]).

-record(state, {
    fd,
	log_data
}).

debug(Format, Args) ->
    {ConsoleMsg, FileMsg, Logdate} = get_log_messages(self(), debug, Format, Args),
    gen_event:sync_notify(error_logger, {ta_debug, ConsoleMsg, FileMsg, Logdate}).

info(Format, Args) ->
    {ConsoleMsg, FileMsg, Logdate} = get_log_messages(self(), info, Format, Args),
    gen_event:sync_notify(error_logger, {ta_info, ConsoleMsg, FileMsg, Logdate}).

warn(Format, Args) ->
    {ConsoleMsg, FileMsg, Logdate} = get_log_messages(self(), warn, Format, Args),
    gen_event:sync_notify(error_logger, {ta_warn, ConsoleMsg, FileMsg, Logdate}).

error(Format, Args) ->
    {ConsoleMsg, FileMsg, Logdate} = get_log_messages(self(), error, Format, Args),
    gen_event:sync_notify(error_logger, {ta_error, ConsoleMsg, FileMsg, Logdate}).

start_link() ->
    ta_event_sup:start_link({local, ta_log}, error_logger, ta_log, []).

stop() ->
    ta_event_sup:stop(ta_log).

init([]) ->
    {Date, _} = msg_time(),
	case get_fielhandle_by_date(Date) of
    {ok, Fd} ->
        {ok, #state{fd = Fd, log_data=Date}};
    {error, Reason} ->
        ReasonStr = file:format_error(Reason),
        io:format("Error opening log file ~s: ~s", [Date ++ ".log", ReasonStr]),
        {stop, {error, ReasonStr, Date ++ ".log"}}
    end.

handle_event({ta_error, ConMsg, FileMsg, Logdate}, #state{log_data=Date, fd=FD} = State) ->
    if 
	   Logdate /= Date ->
         catch(file:close(FD)),	   
         {ok, NewFD} = get_fielhandle_by_date(Logdate),
         log(State#state{log_data=Logdate,fd= NewFD}, ConMsg, FileMsg),
         {ok, State#state{log_data=Logdate,fd= NewFD}};
       true ->	   
         log(State, ConMsg, FileMsg),
        {ok, State}
	end;
	
handle_event({ta_warn, ConMsg, FileMsg, Logdate}, #state{log_data=Date, fd=FD} = State) ->
    if 
	   Logdate /= Date ->
         catch(file:close(FD)),	   
         {ok, NewFD} = get_fielhandle_by_date(Logdate),
         log(State#state{log_data=Logdate,fd= NewFD}, ConMsg, FileMsg),
         {ok, State#state{log_data=Logdate,fd= NewFD}};
       true ->	   
         log(State, ConMsg, FileMsg),
        {ok, State}
	end;
handle_event({ta_info, ConMsg, FileMsg, Logdate}, #state{log_data=Date, fd=FD} = State) ->
    if 
	   Logdate /= Date ->
         catch(file:close(FD)),	   
         {ok, NewFD} = get_fielhandle_by_date(Logdate),
         log(State#state{log_data=Logdate,fd= NewFD}, ConMsg, FileMsg),
         {ok, State#state{log_data=Logdate,fd= NewFD}};
       true ->	   
         log(State, ConMsg, FileMsg),
        {ok, State}
	end;
handle_event({ta_debug, ConMsg, FileMsg, Logdate}, #state{log_data=Date, fd=FD} = State) ->
    if 
	   Logdate /= Date ->
         catch(file:close(FD)),	   
         {ok, NewFD} = get_fielhandle_by_date(Logdate),
         log(State#state{log_data=Logdate,fd= NewFD}, ConMsg, FileMsg),
         {ok, State#state{log_data=Logdate,fd= NewFD}};
       true ->	   
         log(State, ConMsg, FileMsg),
        {ok, State}
	end;
handle_event({error_report, _, {Pid, _, _}}=Event, #state{log_data=Date, fd=FD} = State) ->
    {ConMsg, FileMsg, Logdate} = get_log_messages(Pid, error, "~p", [Event]),
    if 
	   Logdate /= Date ->
         catch(file:close(FD)),	   
         {ok, NewFD} = get_fielhandle_by_date(Logdate),
         log(State#state{log_data=Logdate,fd= NewFD}, ConMsg, FileMsg),
         {ok, State#state{log_data=Logdate,fd= NewFD}};
       true ->	   
         log(State, ConMsg, FileMsg),
        {ok, State}
	end;
handle_event({error, _, {Pid, Format, Args}}, #state{log_data=Date, fd=FD} = State) ->
    {ConMsg, FileMsg, Logdate} = get_log_messages(Pid, error, Format, Args),
    if 
	   Logdate /= Date ->
         catch(file:close(FD)),	   
         {ok, NewFD} = get_fielhandle_by_date(Logdate),
         log(State#state{log_data=Logdate,fd= NewFD}, ConMsg, FileMsg),
         {ok, State#state{log_data=Logdate,fd= NewFD}};
       true ->	   
         log(State, ConMsg, FileMsg),
        {ok, State}
	end;
handle_event(_Event, State) ->
    {ok, State}.

handle_call(_Info, State) ->
    {ok, ok, State}.

handle_info(_Info, State) ->
    {ok, State}.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

terminate(_Arg, #state{fd = Fd}) ->
    file:close(Fd).

log(#state{fd = Fd}, ConsoleMsg, FileMsg) ->
    ok = io:put_chars(ConsoleMsg),
    ok = io:put_chars(Fd, FileMsg).

get_log_messages(Pid, Level, Format, Args) ->
    ConsoleMsg = unicode:characters_to_binary(io_lib:format(
        "[~s] [~p] " ++ Format ++ "~n", [Level, Pid | Args])),
	{Logdate, Time} = msg_time(),
    FileMsg = ["[", Time, "] ", ConsoleMsg],
    {ConsoleMsg, iolist_to_binary(FileMsg), Logdate}.

msg_time() ->
    {{YYYY,MM,DD},{H,M,S}} = calendar:local_time(),
	{lists:flatten(io_lib:format("~4.4.0w-~2.2.0w-~2.2.0w", [YYYY, MM, DD])),
     lists:flatten(io_lib:format("~2.2.0w:~2.2.0w:~2.2.0w", [H, M, S]))}.
	 
get_fielhandle_by_date(Date) ->
    {ok, Path} = file:get_cwd(),
	file:open(Path ++ "/log/" ++ Date ++ ".log", [append]).

